package com.haowu.common.alipay.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.haowu.common.alipay.config.AliCertConfig;
import com.haowu.common.alipay.config.AliPayConfig;
import com.haowu.common.alipay.domain.AlipayRefundDto;
import com.haowu.common.alipay.domain.AlipayTradeDto;
import com.haowu.common.alipay.domain.AlipayTransferDto;
import com.haowu.common.core.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付宝支付工具类
 * @author likeyang
 */
@Slf4j
public class AlipayUtil {

    private static final AliCertConfig ALI_CERT_CONFIG = SpringUtils.getBean(AliCertConfig.class);

    /**
     * 获取支付宝支付回调地址
     */
    public static String getAliNotifyUrl(){
        return ALI_CERT_CONFIG.getAlipayNotifyUrl();
    }

    /**
     * 创建证书模式支付宝请求
     * @return 带有私钥签名的支付宝请求
     */
    public static CertAlipayRequest createDefaultCertAlipayRequest(){
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        //gateway:支付宝网关（固定）https://openapi.alipay.com/gateway.do
        certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do");
        //APPID 即创建应用后生成,详情见创建应用并获取 APPID
        certAlipayRequest.setAppId(AliPayConfig.APPID);
        //开发者应用私钥，由开发者自己生成
        certAlipayRequest.setPrivateKey(AliPayConfig.APP_PRIVATE_KEY);
        //参数返回格式，只支持 json 格式
        certAlipayRequest.setFormat("json");
        //请求和签名使用的字符编码格式，支持 GBK和 UTF-8
        certAlipayRequest.setCharset(AliPayConfig.CHARSET);
        //商户生成签名字符串所使用的签名算法类型，目前支持 RSA2 和 RSA，推荐商家使用 RSA2。
        certAlipayRequest.setSignType(AliPayConfig.SIGN_TYPE);
        //应用公钥（app_cert 文件绝对路径）
        certAlipayRequest.setCertPath(ALI_CERT_CONFIG.getPublicAppCert());
        //支付宝公钥证书（alipay_cert_path 文件绝对路径）
        certAlipayRequest.setAlipayPublicCertPath(ALI_CERT_CONFIG.getAlipayPublicCert());
        //支付宝CA根证书（alipay_root_cert_path 文件绝对路径）
        certAlipayRequest.setRootCertPath(ALI_CERT_CONFIG.getAlipayRootCertPath());
        return certAlipayRequest;
    }

    /**
     * 支付宝转账,用于商家给客户转钱,或者客户/员工提现到支付宝
     * @param transferDto 支付宝转账参数
     * @return 返回map，必定返回success
     */
    public static AlipayFundTransUniTransferResponse alipayTransfer(AlipayTransferDto transferDto){
        //创建支付宝客户端实例化对象AlipayClient
        CertAlipayRequest certAlipayRequest = createDefaultCertAlipayRequest();
        //创建支付宝转账支付请求
        AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
        request.setBizContent(JSON.toJSONString(transferDto));
        log.info(">>>>支付宝转账请求参数:{}", JSONObject.toJSONString(request));
        AlipayFundTransUniTransferResponse response = new AlipayFundTransUniTransferResponse();
        try {
            AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            response = alipayClient.certificateExecute(request);
            log.info(">>>>支付宝转账返回结果:{}", JSONObject.toJSONString(response));
        } catch (AlipayApiException e)  {
            log.error("支付宝请求异常:", e);
        } catch (Exception e){
            log.error("发送请求异常,请检查url地址是否正确,网络是否畅通！", e);
        }
        return response;
    }

    /**
     * 支付宝统一支付接口(公钥证书方式),用于客户购买商品时的支付
     * @param tradeDto 支付宝支付参数
     * @return AlipayTradeAppPayResponse 调用响应结果
     */
    public static AlipayTradeAppPayResponse alipayTrade(AlipayTradeDto tradeDto) {
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("客户付款");
        model.setSubject("订单支付");
        model.setOutTradeNo(tradeDto.getOut_trade_no());
        model.setTimeoutExpress("30m");
        model.setTotalAmount(tradeDto.getTotal_amount());
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(AlipayUtil.getAliNotifyUrl());
        log.info(">>>>支付宝交易请求参数:{}", JSONObject.toJSONString(request));
        //创建支付请求
        CertAlipayRequest certAlipayRequest = createDefaultCertAlipayRequest();
        AlipayTradeAppPayResponse response = new AlipayTradeAppPayResponse();
        try {
            //构造client
            AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            response = alipayClient.sdkExecute(request);
            log.info(">>>>支付宝交易返回参数:{}", JSONObject.toJSONString(response));
        } catch (AlipayApiException e) {
            log.error("支付宝请求异常:", e);
        } catch (Exception e){
            log.error("发送请求异常,请检查url地址是否正确,网络是否畅通！", e);
        }
        return response;
    }

    /**
     * 支付宝统一退款接口(公钥证书方式),用于客户的退款,可通过传参进行部分退款
     * @param alipayRefundDto 支付宝退款参数
     * @return AlipayTradeRefundResponse 调用响应结果
     */
    public static AlipayTradeRefundResponse alipayRefund(AlipayRefundDto alipayRefundDto) {
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        //OutTradeNo:需要退款的订单号,多次部分退款的订单号需保持一致
        model.setOutTradeNo(alipayRefundDto.getOrderCode());
        //OutRequestNo:退款单号,传参表示支持部分退款,多次部分退款的退款单号必须不一致,否则支付宝会抛出异常
        model.setOutRequestNo(alipayRefundDto.getRefundCode());
        //RefundAmount:退款金额,多次部分退款的总金额不能超过支付金额,否则支付宝会抛出异常
        model.setRefundAmount(alipayRefundDto.getRefundAmount());
        request.setBizModel(model);
        log.info(">>>>支付宝退款请求参数:{}", JSONObject.toJSONString(request));
        //创建支付请求
        CertAlipayRequest certAlipayRequest = createDefaultCertAlipayRequest();
        AlipayTradeRefundResponse response = new AlipayTradeRefundResponse();
        try {
            //构造client
            AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            response = alipayClient.certificateExecute(request);
            log.info(">>>>支付宝退款返回参数:{}", JSONObject.toJSONString(response));
        } catch (AlipayApiException e) {
            log.error("支付宝请求异常:", e);
        } catch (Exception e){
            log.error("发送请求异常,请检查url地址是否正确,网络是否畅通！", e);
        }
        return response;
    }

    /**
     * 异步通知参数验签
     * @param notifyRequest 支付宝以post发过来的异步通知请求
     * @return 是否通过验证
     */
    public boolean signVerify(HttpServletRequest notifyRequest){
        Map<String, String> params = new HashMap<>(32);
        Map<String, String[]> requestParams = notifyRequest.getParameterMap();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]: valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        //调用SDK验证签名
        boolean flag = false;
        try {
            flag = AlipaySignature.rsaCheckV1(params, ALI_CERT_CONFIG.getAlipayPublicCert(), AliPayConfig.CHARSET, AliPayConfig.SIGN_TYPE);
        } catch (AlipayApiException e) {
            log.error("回调验签异常:", e);
        } catch (Exception e){
            log.error("发送请求异常,请检查url地址是否正确,网络是否畅通！", e);
        }
        return flag;
    }
}
